#include <hardware/bl0942/bl0942.h>
#include "bl0942.h"

uint8_t readByte();
void sendReadDataCommand();
bool readData();
String processData(unsigned char *data);
u8 calculateCheckNum(unsigned char *data);
double calculateCurrent(unsigned char *data);
double calculateVoltage(unsigned char *data);
double calculatePower(unsigned char *data);
double calculateElectricity(unsigned char *data);

uint8_t datas[23];
StaticJsonDocument<1024> doc;
/**
SCLK_BPS引脚：
SPI 模式时钟输入，UART 模式时可选择波特率。
波特率由硬件管脚SCLK_BPS 决定：
接 0 选择4800bps，
接 1 选择9600bps
 */
void initBl0942()
{
  Serial.begin(9600);
  return;
}

// 读芯片锁，只有一次读全部完成，才能进行下一次读操作
unsigned char chipIsRead = 0;
String readChip()
{
  StaticJsonDocument<1024> doc;
  if (chipIsRead > 0)
  {
    doc["error"] = "chip is reading";
    return docToJson(doc);
  }
  chipIsRead = 1;
  sendReadDataCommand();

  bool flag = readData();
  if (not flag)
  {
    chipIsRead = 0;
    doc["error"] = "read data is wrong";
    return docToJson(doc);
  }

  String jsonStr = processData(datas);
  chipIsRead = 0;
  return jsonStr;
}

String getDebugInfo()
{
  char buffer[300];
  sprintf(buffer, "%02X%02X%02X%02X%02X", datas[0], datas[1], datas[2], datas[3], datas[4]);
  return String(buffer);
}
int getStatus()
{
  return (int)datas[19];
}
String getValueByKey(String key)
{
  return String(doc[key]);
}

// 读取一个字节
uint8_t readByte()
{
  uint8_t reg;
  Serial.readBytes(&reg, 1);
  return reg;
}

/**
 * 请求数据包命令
 * 《BL0942数据手册_V1.05_cn.pdf》3.2.6数据包发送模式：
SSOP10L封装的器件地址是0，即{A2,A1}=00
通过命令{0,1,0,1,1,0,A2,A1}+ 0xAA，BL0942会返回一个全电参数数据包。返回的数据包共22个字节，第23个字节为CHECKSUM

16进制的58对应二进制为：01011000
 */
void sendReadDataCommand()
{
  Serial.write((byte)0x58);
  Serial.write((byte)0xAA);
  return;
}
/**
 * 读取全电参数包：先判断第一个字节为0x55，然后再依次读取接下来的22个字节：
 * 《BL0942数据手册_V1.05_cn.pdf》3.2.6数据包发送模式：
 * 全电参数包格式:
 * 第一个字节为：0x55
 */
bool readData()
{
  if (Serial.available() < 23)
  {
    return false;
  }
  unsigned char retryCount = 0;
  datas[0] = readByte();
  // 目的是获取最新数据
  // 之所以大于46（46是两个完整数据包的长度），因为有时候返回长度为4的数据包，所以长度大于23可能会一直获取不到数据
  while (Serial.available() > 46)
  {
    // 如果datas[0]!= 0x55时，一致循环读取0x55
    while (datas[0] != 0x55)
    {
      ESP.wdtFeed();
      datas[0] = readByte();
      retryCount++;
      if (retryCount > 23)
      {
        return false;
      }
    }
    // 如果datas[0]= 0x55时，但是数据多余
    retryCount = 0;
  }
  // Serial.available()返回结果小于46时，执行下面循环
  while (datas[0] != 0x55)
  {
    ESP.wdtFeed();
    datas[0] = readByte();
    retryCount++;
    if (retryCount > 23)
    {
      return false;
    }
  }
  for (byte i = 1; i < 23; i++)
  {
    datas[i] = readByte();
  }
  return true;
}

String processData(unsigned char *data)
{
  // 计算check_num
  u8 check_num = calculateCheckNum(data);

  if (check_num == data[22])
  {
    double C1 = 0, V1 = 0, P1 = 0, P2 = 0, P3 = 0, E1 = 0;
    // 1、计算实际电流值
    // 全电参数包格式：I_RMS
    // 2^8=256表示向左移位8
    // 2^16=65536表示向左移位16
    C1 = calculateCurrent(data);
    // 2.计算实际电压值
    V1 = calculateVoltage(data);
    // 3.计算实际有功功率值
    P1 = calculatePower(data);
    P2 = V1 * C1;
    if (P2 != 0)
    {
      P3 = P1 / P2;
    }
    // 4.计算上电后BL0942累积电能
    E1 = calculateElectricity(data);
    // char buffer[300];
    // sprintf(buffer, "C=%0.3fA;V=%0.2fV;P1=%0.2fW;P2=%0.2fW;P3=%0.1f;E_con=%0.4fkWh", I1, V1, P1, P2, P3, E1);
    // return String(buffer);
    doc["C1"] = C1;
    doc["V1"] = V1;
    doc["P1"] = P1;
    doc["P2"] = P2;
    doc["P3"] = P3;
    doc["E1"] = E1;
    return docToJson(doc);
  }
  else
  {
    // return String("Check Error");
    StaticJsonDocument<1024> errorDoc;
    errorDoc["error"] = "Check Error";
    return docToJson(errorDoc);
  }
}
u8 calculateCheckNum(unsigned char *data)
{
  u8 i = 0;
  // 十六进制的58对应的十进制数为88
  u32 check_num_temp = 88;
  // checksum=（（{0,1,0,1,1,0,A2,A1} + 0x55 + data1_l + data1_m + data1_h +…….）& 0xff）再按位取反
  for (i = 0; i < 22; i++)
  {
    check_num_temp = check_num_temp + data[i];
  }
  return ~(check_num_temp & 0xFF);
}
double calculateCurrent(unsigned char *data)
{
  u32 I_RMS = data[3] * 65536 + data[2] * 256 + data[1];
  // 2^24=16777216
  if (data[3] & 0x80)
  {
    I_RMS = I_RMS - 16777216;
  }
  // 实际电流值= 电流有效值寄存器值∗Vref/(305978∗RL)安培
  // 计算公式RL单位为毫欧，我们使用电流采样电阻RL为1毫欧
  // Vref=1.218伏
  // I1 = I_RMS * V_REF / (305978 * RL_CURRENT);
  return I_RMS * V_REF / 305978;
}
double calculateVoltage(unsigned char *data)
{
  u32 V_RMS = data[6] * 65536 + data[5] * 256 + data[4];
  // 实际电压值=电压有效值寄存器值∗Vref∗（R2+R1）/(73989∗𝑅1∗1000)伏
  // 计算公式R2,R1单位为K欧,我们使用R2（390K*5），R1（0.51K）
  // Vref=1.218伏
  // R2+R1=390K*5+0.51=1950.51
  // 73989∗𝑅1∗1000=73989∗0.51∗1000=37734390
  // V1 = V_RMS * V_REF * (R2_VOLTAGE + R1_VOLTAGE) / (73989 * R1_VOLTAGE * 1000);
  return V_RMS * V_REF * 1950.51 / 37734390;
}
double calculatePower(unsigned char *data)
{
  u32 WATT = data[12] * 65536 + data[11] * 256 + data[10];
  if (data[12] & 0x80)
  {
    WATT = -(16777216 - WATT);
  }
  // 实际有功功率值=有功功率寄存器值∗Vref^2∗（R2+R1）/ (3537∗RL∗R1∗1000) 瓦
  // 3537∗RL∗R1∗1000=3537∗1∗0.51∗1000=1803870
  // P1 = WATT * V_REF * V_REF * (R2_VOLTAGE + R1_VOLTAGE) / (3537 * RL_CURRENT * R1_VOLTAGE * 1000);
  return WATT * V_REF * V_REF * 1950.51 / 1803870;
}
double calculateElectricity(unsigned char *data)
{
  u32 CF_CNT = data[15] * 65536 + data[14] * 256 + data[13];
  // 每个电能脉冲对应的电量=1638.4∗256∗Vref^2∗（R2+R1）/(3600000∗3537∗RL∗R1∗1000)度
  // 上电后BL0942累积电能=每个电能脉冲对应的电量 ∗ 1200  度
  // 1638.4∗256∗Vref^2∗（R2+R1） = 1638.4∗256∗1.218∗1.218∗1950.51 = 1213675716.105732
  // 3600000∗3537∗RL∗R1∗1000= 3600000∗3537∗1∗0.51∗1000 = 6,493,932,000,000
  // 每个电能脉冲对应的电量 = 1/5350.632
  return CF_CNT / 5350.632;
}